﻿using System.Reflection;

using Mono.Cecil;
using Mono.Collections.Generic;

public static class TypeDefinitionMixins
{
    public static bool IsExcluded(this TypeDefinition type)
    {
        if (type.Name == "<Module>")
            return true;
        if (type.IsBaseOf<Attribute>())
            return true;
        if (type.Attributes == Mono.Cecil.TypeAttributes.Interface)
            return true;
        return false;
    }
    public static bool IsBaseOf<T>(this TypeDefinition self)
    {
        if (self == null)
            return false;
        if (self.BaseType == null)
            return false;
        if (self.BaseType.FullName != typeof(T).FullName)
            return false;
        return true;
    }
    private static T? FindAttribute<T>(this Collection<CustomAttribute> attributes) where T : Attribute
    {
        const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
        var attribyteType = typeof(T);
        var fieldInfos = attribyteType.GetFields(flags);
        var attrs = from attr in attributes where attr.AttributeType.FullName == attribyteType.FullName select attr;
        foreach (var attr in attrs)
        {
            if (attr.HasConstructorArguments)
            {
                var ctors = attribyteType.GetConstructors();
                foreach (var ctor in ctors)
                {
                    ParameterInfo[] array = ctor.GetParameters();
                    if (array.Length == attr.ConstructorArguments.Count)
                    {
                        object[] values = new object[array.Length];
                        bool thenIs = true;
                        for (int i = 0; i < array.Length; i++)
                        {
                            ParameterInfo param = array[i];
                            if (param.ParameterType.FullName != attr.ConstructorArguments[i].Type.FullName)
                            {
                                thenIs = false;
                                break;
                            }
                            values[i] = attr.ConstructorArguments[i].Value;
                        }
                        if (thenIs)
                        {
                            return (T)ctor.Invoke(values);
                        }
                    }
                }
            }
        }
        return null;
    }
    public static T? FindAttribute<T>(this MethodDefinition self) where T : Attribute
    {
        return FindAttribute<T>(self.CustomAttributes);
    }
    public static T? FindAttribute<T>(this FieldDefinition self) where T : Attribute
    {
        return FindAttribute<T>(self.CustomAttributes);
    }
    public static T? FindAttribute<T>(this TypeDefinition self) where T : Attribute
    {
        return FindAttribute<T>(self.CustomAttributes);
    }
    public static bool HasCustomAttribute<T>(this Collection<CustomAttribute> attributes) where T : Attribute
    {
        var fullname = typeof(T).FullName;
        return attributes.Any(a => a.AttributeType.FullName == fullname);
    }
    public static bool HasCustomAttribute<T>(this ParameterDefinition self) where T : Attribute
    {
        return self.CustomAttributes.HasCustomAttribute<T>();
    }
    public static bool HasCustomAttribute<T>(this FieldDefinition self) where T : Attribute
    {
        return self.CustomAttributes.HasCustomAttribute<T>();
    }
    public static bool HasCustomAttribute<T>(this MethodDefinition self) where T : Attribute
    {
        return self.CustomAttributes.HasCustomAttribute<T>();
    }
    public static bool HasCustomAttribute<T>(this TypeDefinition self) where T : Attribute
    {
        return self.CustomAttributes.HasCustomAttribute<T>();
    }
    public static ObjectKind ToStackType(this TypeReference type)
    {
        if (type == null)
            return ObjectKind.Unknown;
        switch (type.FullName)
        {
            case "System.Void":
                return ObjectKind.Void;
            case "System.Boolean":
                return ObjectKind.Boolean;
            case "System.SByte":
            case "System.Int16":
            case "System.Int32":
            case "System.Int64":
            case "System.Byte":
            case "System.UInt16":
            case "System.UInt32":
            case "System.UInt64":
                return ObjectKind.Integer;
            case "System.Single":
            case "System.Double":
                return ObjectKind.Decimal;
            default:
                return ObjectKind.Object;
        }
    }
}

